import asyncio
import datetime
import ssl

from aiortc.rtcrtpsender import RTCRtpSender
import json

import websockets
from aiortc import RTCPeerConnection, RTCSessionDescription, RTCConfiguration, RTCIceServer
from aiortc.contrib.media import MediaRelay, MediaPlayer

from logger import log
from settings import settings
from snapshot import snapshot

pcs = {}
relay = None


def getRtspUrl(rtsp_code):
    # 遍历列表，寻找匹配的id
    for item in settings.streams:
        if item['id'] == int(rtsp_code.split("_")[-1]):
            # 如果找到匹配的id，获取对应的rtsp
            rtsp_url = item['rtsp']
            if item['cameraRtsp'] is not None and item['cameraRtsp'] != '':
                rtsp_url = item['cameraRtsp']
            return rtsp_url
    else:
        log.info('No matching id found in the list.')


def create_local_tracks(params):
    try:
        global relay
        if relay is None:
            relay = MediaRelay()
        # begin 调用本地摄像头可以正常用
        # options = {"framerate": "30", "video_size": "640x480"}
        # webcam = MediaPlayer("default:none", format="avfoundation", options=options)
        # begin 使用 RTSP URL 创建 MediaPlayer
        rtsp_url = getRtspUrl(params['rtsp_code'])
        webcam = MediaPlayer(rtsp_url, format='rtsp', options={'rtsp_transport': 'tcp',
                                                               'framerate': '20',
                                                               'max_delay': '5000000',
                                                               'video_size': '1280x720'}, timeout=5)
        pcs[params['from']]['relay'] = relay
        pcs[params['from']]['webcam'] = webcam
        return None, relay.subscribe(webcam.video, True)
    except Exception as e:
        log.exception(e)
    return None, None


def force_codec(pc, sender, forced_codec):
    kind = forced_codec.split("/")[0]
    codecs = RTCRtpSender.getCapabilities(kind).codecs
    transceiver = next(t for t in pc.getTransceivers() if t.sender == sender)
    transceiver.setCodecPreferences(
        [codec for codec in codecs if codec.mimeType == forced_codec]
    )


async def onOffer(ws, params):
    log.info("offer")
    pc = RTCPeerConnection(RTCConfiguration([RTCIceServer("turn:39.106.154.90:3478", "lipeng", "123456")]))
    # pc = RTCPeerConnection()
    from_ = params["from"]
    pcs[from_] = {}
    pcs[from_]['pc'] = pc

    @pc.on("connectionstatechange")
    async def on_connectionstatechange():
        log.info(f"Connection state is {pc.connectionState} for {from_}")
        if pc.connectionState == "failed":
            await pcs[from_]['pc'].close()
            webcam_ = pcs[from_]['webcam']
            if webcam_.video:
                webcam_.video.stop()
            if webcam_.audio:
                webcam_.audio.stop()
            pcs[from_]['relay'] = None
            pcs[from_]['webcam'] = None
            pcs.pop(from_)
            log.info(f"poped: {pcs}")

    # open media source
    audio, video = create_local_tracks(params)

    # 加上会报错
    # if audio:
    #     pc.addTrack(audio)

    if video:
        # pc.addTrack(ObjectDetectionTrack(video, polygon_points))
        video_sender = pc.addTrack(video)
        if settings.force_codec:
            force_codec(pc, video_sender, settings.force_codec)

    await pc.setRemoteDescription(RTCSessionDescription(sdp=params['offer']["sdp"], type=params['offer']["type"]))

    answer = await pc.createAnswer()
    await pc.setLocalDescription(answer)

    answer_rsp = {
        "type": "answer",
        "answer": {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type},
        "from": settings.code,
        "to": from_
    }
    log.info(f"Answer Response")
    await ws.send(json.dumps(answer_rsp))


async def onSnapshot(ws, loads):
    key = loads['device']
    stream_url = getRtspUrl(key)
    image_bytes = snapshot.capture_image_from_rtsp(stream_url)
    url = snapshot.upload_image(image_bytes)
    message = {
        "type": "callback",
        "from": settings.code,
        "to": loads["from"],
        "code": "snapshot",
        "device": key,
        "uuid": loads["uuid"],
        "snapshotUrl": url
    }
    await ws.send(json.dumps(message))


async def onCommand(ws, loads):
    log.info(f"onCommand: {loads}")
    if loads['code'] == 'snapshot':
        await onSnapshot(ws, loads)


def onPing(ws, loads):
    # log.info("ping")
    global last_timestamp
    last_timestamp = datetime.datetime.now()


async def onMessage(ws, message):
    log.info("Received message:{}".format(message))
    loads = json.loads(message)
    if loads['type'] == 'offer':
        await onOffer(ws, loads)
    elif loads['type'] == 'command':
        await onCommand(ws, loads)
    elif loads['type'] == 'ping':
        onPing(ws, loads)


async def heartbeat(websocket):
    while True:
        # 检测时间差
        await asyncio.sleep(10)  # 每10秒检查一次
        if (datetime.datetime.now() - last_timestamp).seconds > 30:
            # 如果超过30秒没有收到消息，则尝试重连
            log.info("No message in the last 30 seconds, reconnecting...")
            await websocket.close()
            break
        # 发送心跳消息
        try:
            message = {
                "type": "ping",
                "from": settings.code,
                "to": settings.code
            }
            await websocket.send(json.dumps(message))
        except Exception as e:
            log.info(f"An error occurred during heartbeat: {e}")
            break


async def socket_logic():
    # 连接信令服务器
    # uri = "ws://localhost:9443/websocketRTC/" + settings.code
    # uri = "wss://webrtc.wechain360.com/websocketRTC/" + settings.code
    host = settings.host
    webrtc_host = ""
    if host == 'platform.ncexc-rm.com':
        webrtc_host = 'vs.ncexc-rm.com'
    else:
        webrtc_host = host.replace('platform', 'webrtc', 1)
    uri = "wss://" + webrtc_host + "/websocketRTC/" + settings.code
    log.info(f"uri:{uri}")
    ssl_context = ssl.create_default_context()
    ssl_context.check_hostname = False
    ssl_context.verify_mode = ssl.CERT_NONE
    # uri = "wss://mq.wechain360.com:8443/websocketRTC/" + settings.code
    while True:
        try:
            async with websockets.connect(uri, ssl=ssl_context) as websocket:
                global last_timestamp
                last_timestamp = datetime.datetime.now()
                # 启动心跳任务
                heartbeat_task = asyncio.create_task(heartbeat(websocket))
                log.info("WebSocket connection opened")
                while True:
                    message = await websocket.recv()
                    await onMessage(websocket, message)
        except websockets.exceptions.ConnectionClosed as e:
            log.info(f"WebSocket connection closed with exception: {e}")
        except Exception as e:
            log.info(f"Exception: {e}")
        finally:
            # 等待时间检查任务结束
            if 'heartbeat_task' in locals():
                heartbeat_task.cancel()
                # await heartbeat_task
        await asyncio.sleep(5)  # 等待一段时间后再次尝试连接，避免无限快速重连
